import * as THREE from '../../../../modules/three/three.module.js'
import Earcut from "./Earcut.js"
// import {BoolPolygonsUtil} from "./bool-polygons-util.js";
import {d2r, mapColorCssToCode, r2d, WorldConfig} from "../CommonConsts.js";
import {vertsToCircles} from "./ToolMethods.js";

const Config = {
	showLineSpan: 0.0001, //  两个数字之差的绝对值小于这个数字，就视为相等
	adhereSpan: 0.1,  //吸附距离
}

const minimum = Config.showLineSpan;

export function unPreciseIndexOf(arr, num) {
	for (let i = 0; i < arr.length; i++) {
		let iNum = arr[i]
		if (Math.abs(iNum - num) <= minimum) {
			return i
		}
	}
	return -1
}

export function flatten(vertices) {
	let size = vertices[0].z != undefined ? 3 : 2;
	let arr = new Float32Array(vertices.length * size);
	let count = 0;
	vertices.forEach(vector => {
		arr[count++] = vector.x;
		arr[count++] = vector.y;
		if (size == 3)
			arr[count++] = vector.z;
	});
	return arr;
}

export function interpolatePoints(start, end, stepSize) {
	let distance = start.distanceTo(end)
	const alpha = stepSize / distance
	const target = [start]
	let progress = alpha
	while (progress < 1) {
		const point = start.clone().lerp(end, progress)
		progress += alpha
		target.push(point)
	}
	target.push(end)
	return target
}

function computeFontSize(str, size, family) {
	let spanDom = document.createElement("span");
	spanDom.style.fontSize = size;
	spanDom.style.opacity = "0";
	spanDom.style.fontFamily = family;
	spanDom.innerHTML = str;
	document.body.append(spanDom);
	let sizeD = {};
	sizeD.width = spanDom.offsetWidth;
	sizeD.height = spanDom.offsetHeight;
	spanDom.remove();
	return sizeD;
}

export function drawCircle() {
	const canvas = document.createElement( 'canvas' );
	canvas.width = 64;
	canvas.height = 64;
	const ctx = canvas.getContext( '2d' );
	ctx.beginPath();
	ctx.arc(32,32,20,0,2*Math.PI);
	ctx.strokeStyle = '#fff';
	ctx.lineWidth = 16;
	ctx.stroke();
	return canvas;
}

function drawEyeLocatorIcon(size) {
	const w = size;
	const p0 = { x: 0.25 * w, y: 0.2 * w };
	const p1 = { x: 0.75 * w, y: 0.2 * w };
	const p2 = { x: (5 / 8) * w, y: (3 / 8) * w };
	const p3 = { x: (3 / 8) * w, y: (3 / 8) * w };

	const a0 = { x: (1 / 8) * w, y: 0.4 * w };
	const a1 = { x: (7 / 8) * w, y: 0.4 * w };
	const a2 = { x: (7 / 8) * w, y: 0.8 * w };
	const a3 = { x: (1 / 8) * w, y: 0.8 * w };
	const canvas = document.createElement('canvas');
	const ctx = canvas.getContext('2d');
	canvas.height = w;
	canvas.width = w;

	const fillContour = (ctx, points) => {
		ctx.beginPath();
		ctx.moveTo(points[0].x, points[0].y);
		for (let i = 1; i < points.length; i++) {
			ctx.lineTo(points[i].x, points[i].y);
		}
		ctx.closePath();
		ctx.fillStyle = '#fff';
		ctx.fill();
		ctx.strokeStyle = '#000';
		ctx.stroke();
	};
	fillContour(ctx, [p0, p1, p2, p3]);
	fillContour(ctx, [a0, a1, a2, a3]);
	return canvas;
}

function drawPosMarker(radius, lineWidth, arrowHeight, color, txtColor, text) {
	let canvas = document.createElement( 'canvas' );
	let ctx = canvas.getContext( '2d' );
	canvas.height = (radius + lineWidth) * 4;
	canvas.width = (radius + lineWidth) * 4;
	let hw = canvas.width / 2;
	let g = radius + lineWidth/2, h = arrowHeight;
	let vOffset = g + h;
	let f = Math.sqrt(h*h + 2*h*g);
	let e = f * g / (h + g);
	let d = Math.sqrt(g*g - e*e);
	e /= 1.5;
	let p0 = {x: hw, y: hw + g + h - vOffset};
	let p1 = {x: hw + e, y: hw + d - vOffset}, p2 = {x: hw - e, y: hw + d - vOffset};
	ctx.beginPath();
	ctx.moveTo(p0.x, p0.y);
	ctx.lineTo(p1.x, p1.y);
	ctx.lineTo(p2.x, p2.y);
	ctx.closePath();
	ctx.fillStyle = color;
	ctx.fill();

	ctx.beginPath();
	// ctx.fillStyle = color;
	ctx.strokeStyle = color;
	ctx.lineWidth = lineWidth;
	ctx.arc(canvas.width/2, canvas.height/2 - vOffset + 5, radius, Math.PI*2, false);
	// ctx.fill();
	ctx.stroke();
	if (text) {
		let textColor = txtColor || "#d25b21";
		let size = computeFontSize(text, "50px", "Times New Roman");
		ctx.font = "50px Times New Roman";
		ctx.fillStyle = textColor;
		ctx.fillText(text, hw - size.width/2, hw + size.height/3 - vOffset);
		// ctx.lineWidth = 1;
		// ctx.strokeStyle = "#d25b21";
		// ctx.strokeText(text, hw, hw);
	}
	return canvas;
}

export function createEyeLocatorMarker() {
	const canvas = drawEyeLocatorIcon(128);
	const sprite = makeCanvasTextSprite(canvas);
	sprite.center.y = 0.8;
	sprite.scale.set(256 / window.innerHeight, 256 / window.innerHeight, 1.0);
	sprite.userData.scalePersp = new THREE.Vector3().copy(sprite.scale);
	sprite.userData.scaleOrtho = new THREE.Vector3()
		.copy(sprite.scale)
		.multiply(new THREE.Vector3(4, 4, 1));
	return sprite;
}

export function getPositionMarker(text, color, txtColor) {
	let markDefaultColor = "#ff0000";
	color = color || markDefaultColor;
	let canvas = drawPosMarker(30, 18, 21, color, txtColor, text);
	let sprite = makeCanvasSprite(canvas);
	return sprite;
}

//根据canvas图形制作sprite
export function makeCanvasSprite(canvas) {
	const texture = new THREE.Texture(canvas);
	texture.needsUpdate = true;
	const spriteMaterial = new THREE.SpriteMaterial({map : texture, sizeAttenuation: false});
	const sprite = new THREE.Sprite(spriteMaterial);
	sprite.center = new THREE.Vector2(0.5, 0.5);
	sprite.scale.set(canvas.width/window.innerWidth, canvas.height/window.innerWidth, 1.0);
	// sprite.material.depthWrite = false;
	sprite.material.depthTest = false;
	sprite.renderOrder = 9
	return sprite;
}

const locationTexture = new THREE.TextureLoader().load(
	'/three/static/3dAssets/texture/location.png',
);
export function createLocationSprite() {
	const spriteMaterial = new THREE.SpriteMaterial({
		map: locationTexture,
		sizeAttenuation: false,
	});
	const sprite = new THREE.Sprite(spriteMaterial);
	sprite.center = new THREE.Vector2(0.48, 0.2);
	sprite.scale.set(128 / window.innerWidth, 128 / window.innerWidth, 1.0);
	// sprite.material.depthWrite = false;
	sprite.material.depthTest = false;
	sprite.renderOrder = 10;
	return sprite;
}

export function bufferAttributeToVectors(attr) {
	const target = []
	for (let i = 0; i < attr.count; i++) {
		let v
		switch(attr.itemSize) {
			case 2:
				v = new THREE.Vector2().fromBufferAttribute(attr, i)
				break
			case 3:
				v = new THREE.Vector3().fromBufferAttribute(attr, i)
				break
			case 4:
				v = new THREE.Vector4().fromBufferAttribute(attr, i)
				break
		}
		target.push(v)
	}
	return target
}
export function bufferAttributeToVectorsV2(attr, target) {
	for (let i = 0; i < attr.count; i++) {
		const vector = target[i]
		vector.fromBufferAttribute(attr, i)
	}
	return target
}
export function updatePosAttr(geometry, index, pos) {
	const attr = geometry.getAttribute('position')
	attr.setXYZ(index, pos.x, pos.y, pos.z)
	attr.needsUpdate = true
}

export function vector3sToFloat32BufferAttribute(vector3s) {
	return new THREE.Float32BufferAttribute(flatten(vector3s), 3);
}

// 归一化uv坐标
export function normalizeUvVectors(uvVectors) {
	const box2 = new THREE.Box2().setFromPoints(uvVectors)
	const {min, max} = box2
	const w = max.x - min.x
	const h = max.y - min.y
	const size = w > h ? w : h
	const offset = min.clone().add(max).divideScalar(2)
	const vector2sNormalized = []
	uvVectors.forEach(point => {
		const u = (point.x - offset.x) / size + 0.5
		const v = (point.y - offset.y) / size + 0.5
		vector2sNormalized.push(u, v)
	})
	return vector2sNormalized
}

export function getPolygonGeometry(polygon, planeMat, facing) {
	let vectices = polygon[0], holeIndices = polygon[1]
	let arr2d = []
	const normalVectors = []
	vectices.forEach(it => {
		arr2d.push(it.x, it.y)
		normalVectors.push(facing)
	})
	let triangles = Earcut.triangulate(arr2d, holeIndices)
	let geometry = new THREE.BufferGeometry()
	const uvVectors = []


	const vectors = vectices.map(it => {
		const z = it.z || 0
		const v = new THREE.Vector3(it.x, it.y, z)
		uvVectors.push(new THREE.Vector2(it.x, it.y))
		return v.applyMatrix4(planeMat)
	})
	geometry.setAttribute("position", new THREE.BufferAttribute(flatten(vectors), 3))
	geometry.setAttribute("normal", new THREE.BufferAttribute(flatten(normalVectors), 3))
	const uvVectorsNormalized = normalizeUvVectors(uvVectors)
	geometry.setAttribute("uv", new THREE.BufferAttribute(new Float32Array(uvVectorsNormalized), 2))
	geometry.setIndex(triangles)
	geometry.computeBoundingSphere()
	geometry.computeBoundingBox()
	return geometry;
}

function drawGridOrigin(size, color) {
	let canvas = document.createElement( 'canvas' );
	let ctx = canvas.getContext( '2d' );
	canvas.height = size*2;
	canvas.width = size*2;

	let p0 = {x: size*2, y: size}, p1 = {x: size, y: 0};
	let p2 = {x: 0, y: size}, p3 = {x: size, y: size*2};
	ctx.beginPath();
	ctx.moveTo(p0.x, p0.y);
	ctx.lineTo(p1.x, p1.y);
	ctx.lineTo(p2.x, p2.y);
	ctx.lineTo(p3.x, p3.y);
	ctx.closePath();
	ctx.fillStyle = color;
	ctx.fill();

	return canvas;
}
export function getGridOrigin(size, color) {
	let canvas = drawGridOrigin(size, color);
	let sprite = makeCanvasSprite(canvas);
	sprite.material.transparent = true;
	return sprite;
}
//根据文字生产canvas, textOption: {size: 40, textColor: '#000', outlineColor: '#fff', outline: true}
export function generateTextCanvas(text, textOption) {
	let option = textOption || {};
	let canvas = document.createElement( 'canvas' );
	let context = canvas.getContext( '2d' );
	let size = option.size || 50;
	let fontStr = 'normal 400 ' + size + 'px Microsoft YaHei';
	context.font = fontStr;
	let strLst = text.split('\n');
	let maxWdith = -Infinity;
	for (let str of strLst) {
		let measured = context.measureText(str);
		if (maxWdith < measured.width) {
			maxWdith = measured.width;
		}
	}
	canvas.width = maxWdith + 20;      //根据文字内容获取宽度
	let lineHeight = size * 1.5;  // fontsize * 1.5
	canvas.height = lineHeight * strLst.length;
	// let obj = computeFontSize(text, '40px', 'Microsoft YaHei');
	// console.log("generateCanvas, ", obj);
	let offset = 1.8 * size / 40;
	const hasOutline = option.outline || true
	strLst.forEach((str, index) => {
		context.beginPath();
		let x = 10, y = size * (1 + index);
		context.font = fontStr;
		if (hasOutline) {
			context.fillStyle = option.outlineColor || "#fff";
			context.fillText(str, x-offset, y+offset);
			context.fillText(str, x+offset, y+offset);
			context.fillText(str, x+offset, y-offset);
			context.fillText(str, x-offset, y-offset);
		}
		context.fillStyle = option.textColor || "#000";
		context.fillText(str, x, y);
		context.fill();
	});

	// return convertToSDF(canvas);
	return canvas;
}

export function makeCanvasTextSprite(canvas) {
	let texture = new THREE.Texture(canvas);
	texture.minFilter = THREE.LinearFilter;
	texture.needsUpdate = true;
	let spriteMaterial = new THREE.SpriteMaterial({map : texture, sizeAttenuation: false});
	let sprite = new THREE.Sprite(spriteMaterial);
	sprite.center = new THREE.Vector2(0.5, 0);
	let poi = {w: canvas.width, h: canvas.height};
	sprite.scale.set(poi.w/(window.innerHeight*2.0), poi.h/(window.innerHeight*2.0), 1.0);
	sprite.material.depthWrite = false;
	sprite.material.depthTest = false;
	sprite.material.transparent = true;
	sprite.userData.poi = poi;
	sprite.renderOrder = 9
	return sprite;
}

export function createTextSprite(txt, txtOption) {
	const canvas = generateTextCanvas(txt, txtOption)
	const sprite = makeCanvasTextSprite(canvas)
	return sprite
}

//----------------------------------------------------------
/**
 * three的零变换坐标转换为 cesium地球 的 三维笛卡尔坐标；等于绕x轴旋转正的 90° 的变换
 * @param {{x: number, y: number, z: number}} coord three的零变换坐标
 * @return {{x: number, y: number, z: number}} cesium地球 的 三维笛卡尔坐标
 */
export function toEarthCoord(coord) {
	const tmp = coord.y
	coord.y = -coord.z
	coord.z = tmp
	return coord
}

// toEarthCoord的反函数
export function cesiumCoordToThree(coord) {
	const tmp = coord.y
	coord.y = coord.z
	coord.z = -tmp
	return coord
}

export const PI2 = Math.PI * 2

/**
 * 获取到 正Y轴到目标点的顺时针夹角
 * @return 返回值范围是[0, 360)
 */
export function getYAngleDeg(x, y) {
	let a = Math.atan2(x, y)
	if (a > PI2) {
		a -= PI2
	}
	if (a < 0) {
		a += PI2
	}
	return a * r2d;
}

/**
 * getYAngleDeg 的反函数
 * @param deg 正Y轴到目标点的顺时针夹角, 度数
 */
export function degYToCoord(deg) {
	const angle = (90 - deg) * d2r;
	return [Math.cos(angle), Math.sin(angle)];
}

/**
 * @return 返回值范围是[0, PI * 2)
 */
function getAngle(x, y) {
	let a = Math.atan2(y, x)
	if (a > PI2) {
		a -= PI2
	}
	if (a < 0) {
		a += PI2
	}
	return a
}

/**
 * @return 返回值范围是(-PI, PI]
 */
export function angleDiff(srcAngle, dstAngle) {
	let diff = dstAngle - srcAngle
	if (diff > Math.PI) {
		diff -= PI2
	} else if (diff <= -Math.PI) {
		diff += PI2
	}
	return diff
}

/**
 * 获取 oldDir 旋转多少弧度后 可以和 newDir 重合
 * @param {{x: number, y: number}} oldDir
 * @param {{x: number, y: number}} newDir
 * @return {number} 弧度
 */
export function getOldToNewAngle(oldDir, newDir) {
	const angle1 = getAngle(newDir.x, newDir.y)
	const angle2 = getAngle(oldDir.x, oldDir.y)
	return angleDiff(angle2, angle1)
}

export function createGuideLine (vertices, lineColor) {
	let lGeometry = new THREE.BufferGeometry()
	let lineMaterial = new THREE.LineDashedMaterial({ color: lineColor, dashSize: 0.09, gapSize: 0.06 })
	lineMaterial.transparent = true
	let typedArr = flatten(vertices)
	lGeometry.setAttribute("position", new THREE.BufferAttribute(typedArr, 3))
	let lineMesh = new THREE.Line(lGeometry, lineMaterial)
	lineMesh.computeLineDistances()
	return lineMesh
}

export function createContourLine(vector2s, z = 0) {
	const lineMat = new THREE.LineBasicMaterial({
		color: '#d40099',
		toneMapped: false,
		transparent: true,
		depthTest: false,
		depthWrite: false,
	})
	const vector3s = vector2s.map(it => {
		if (it.z) {
			return new THREE.Vector3(it.x, it.y, it.z)
		}
		return new THREE.Vector3(it.x, it.y, z)
	})
	if (vector2s.length > 2) {
		vector3s.push(vector3s[0])
	}
	const lineGeo = new THREE.BufferGeometry()
	lineGeo.setAttribute('position', new THREE.BufferAttribute(flatten(vector3s), 3))
	const lineSegment = new THREE.Line(lineGeo, lineMat)
	return lineSegment
}

export function createContourLineSegments(vector3s, color = '#d40099') {
	const lineMat = new THREE.LineBasicMaterial({
		color: color,
		transparent: true,
		depthTest: false,
		depthWrite: false,
	})
	const lineGeo = new THREE.BufferGeometry()
	lineGeo.setAttribute('position', new THREE.BufferAttribute(flatten(vector3s), 3))
	const lineSegment = new THREE.LineSegments(lineGeo, lineMat)
	return lineSegment
}

export function box2Intersects(a, b) {
	// if (!a.intersectsBox(b)) {
	// 	return false
	// }
	const intersected = a.max.x < b.min.x || a.min.x > b.max.x ||
		a.max.y < b.min.y || a.min.y > b.max.y ? false : true
	if (!intersected) {
		return false
	}
	// 针对特殊情况的排除： 两个盒子正好接触
	if (a.min.x === b.max.x || b.min.x === a.max.x) {
		return false
	}
	if (a.min.y === b.max.y || b.min.y === a.max.y) {
		return false
	}
	// if (a.min.z === b.max.z || b.min.z === a.max.z) {
	// 	return false
	// }
	return true
}

// https://www.jianshu.com/p/39328e0805a0
/**
 * 采用 格雷汉姆 寻找点集合 points0 的凸包
 * @param {THREE.Vector2[]} points0
 * @return {THREE.Vector2[]} 凸包轮廓
 */
export function findConvexHull(points0) {
	const points = SamplePointReduce(points0);
	const p0 = findPivot(points);
	points.splice(points.indexOf(p0), 1);
	const vectors = points.map(it => it.clone().sub(p0));
	// 幅角排序，数学二维坐标系中，和正X轴的逆时针夹角越小，排在越前面，
	vectors.sort((a, b) => {
		const diff = getAngle(a.x, a.y) - getAngle(b.x, b.y);
		if (diff === 0) {
			// a 和 b 两个方向相同时，更短的向量排在更前面；
			return a.lengthSq() - b.lengthSq();
		}
		return diff;
	});
	vectors.forEach(it => it.add(p0));
	const p1 = vectors.shift();
	const convexHull = [p0, p1];
	while(vectors.length) {
		const p = vectors.shift();
		let leftSide = false;
		while (!leftSide) {
			const v1 = convexHull[convexHull.length - 1];
			const v0 = convexHull[convexHull.length - 2];
			leftSide = onLeftSideOrOn(v0, v1, p);
			if (leftSide) {
				convexHull.push(p);
			} else {
				convexHull.pop();
			}
		}
	}
	return convexHull;
}

/**
 * @param {THREE.Vector2} v0, 方向起点
 * @param {THREE.Vector2} v1, 方向终点
 * @param {THREE.Vector2} p, 待考察的点，返回true时，p点 入栈，false时，出栈
 * @return true 当 p点 在v01 左侧 或 正好在v01的延长线时
 */
function onLeftSideOrOn(v0, v1, p) {
	const v01 = v1.clone().sub(v0);
	const v0p = p.clone().sub(v0);
	return v01.cross(v0p) >= 0;
}

// 找到具有最小y坐标的点作为基准点
function findPivot(points) {
	let pivot = points[0];
	for (const point of points) {
		if (point.y < pivot.y || (point.y === pivot.y && point.x < pivot.x)) {
			pivot = point;
		}
	}
	return pivot;
}

// 采样点去重
function SamplePointReduce(point) {
	const pointMap = new Map();
	point.forEach((it) => {
		const id = it.x.toFixed(4) + '_' + it.y.toFixed(4);
		pointMap.set(id, new THREE.Vector2(it.x, it.y));
	});

	return [...pointMap.values()];
}

const screenDefault = {
	left: 0,
	top: 0,
	width: window.innerWidth,
	height: window.innerHeight,
}

export function getMouseRaycaster(raycaster, camera, x, y, screen = screenDefault) {
	// let vector = new THREE.Vector3(( ( x - screen.left ) / screen.width ) * 2 - 1, -( ( y - screen.top ) / screen.height ) * 2 + 1, 0.5);
	// vector = vector.unproject(camera);
	// const ray = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize())
	// return ray
	// const raycaster = new THREE.Raycaster()
	raycaster.params.Line.threshold = 0.1
	const screenPos = canvasCoordToScreenCoord({x, y}, screen)
	raycaster.setFromCamera(screenPos, camera)
	return raycaster
}

/**
 * 将一个canvas坐标系中的点坐标归一化到[-1, 1]的区间，并且右和上为正方向
 * @param {{x:number, y:number}} point
 * @return {{x:number, y:number}}
 */
export function canvasCoordToScreenCoord(point, screen = screenDefault) {
	const px = point.x - screen.left
	const py = point.y - screen.top
	return {
		x: px / (screen.width * 1) * 2 - 1,
		y: -py / (screen.height * 1) * 2 + 1,
	}
}

const _tempPos = new THREE.Vector3()
// 3d世界坐标转为屏幕坐标
export function coord3dToScreenPos(position, camera, canvasWidth, canvasHeight) {
	_tempPos.x = position.x
	_tempPos.y = position.y
	_tempPos.z = position.z
	const standardVector = _tempPos.project(camera) // 世界坐标转标准设备坐标， 标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间
	const a = canvasWidth / 2
	const b = canvasHeight / 2
	const x = Math.round(standardVector.x * a + a)// 标准设备坐标转屏幕坐标
	const y = Math.round(-standardVector.y * b + b)// 标准设备坐标转屏幕坐标
	return {x: x, y: y}
}

/**
 * arr0 中的数值向 arr1偏移，找出arr0 中和 arr1 距离最小的 一对数字
 * @param {number[]} arr0
 * @param {number[]} arr1
 * @return {null|{adhereTo: number[], span: number}}
 */
export function getClosestSpan (arr0, arr1) {
	let minSpanAbs = Infinity;
	let minSpan;
	let equalNums = [];
	let adhereTo = []
	arr1.forEach(num1 => {
		arr0.forEach(num0 => {
			const span = num1 - num0;
			const spanAbs = Math.abs(span);

			if (spanAbs < Config.showLineSpan) {
				equalNums.push(num1);
			} else {
				if (spanAbs <= Config.adhereSpan) {
					const float1 = Math.floor(num1 * 10000) / 10000;
					if (minSpanAbs > spanAbs) {
						if (minSpanAbs - spanAbs < Config.showLineSpan) {
							if (adhereTo.indexOf(float1) < 0) {
								adhereTo.push(float1);
							}
						} else {
							adhereTo = [float1];
						}
						minSpanAbs = spanAbs;
						minSpan = span;
					} else if (spanAbs - minSpanAbs < Config.showLineSpan) {
						if (adhereTo.indexOf(float1) < 0) {
							adhereTo.push(float1);
						}
					}
				}
			}
		})
	})
	if (equalNums.length > 0) {
		return { adhereTo: equalNums, span: 0 };
	}
	if (minSpanAbs < Infinity) {
		return { adhereTo: adhereTo, span: minSpan };
	}
	return null;
}

/**
 * 提取工作区中的纵横吸附线
 * @param {WorkArea} workArea
 * @param {number[]} hLines 输出参数，水平吸附线
 * @param {number[]} vLines 输出参数，竖直吸附线
 */
export function retrieveAdhereLines(workArea, hLines, vLines) {
	const circles = vertsToCircles(workArea.verts)
	circles.forEach(circle => {
		circle.forEach((point, index) => {
			const nextIndex = (index + 1) % circle.length;
			const to = circle[nextIndex];
			const from = point;
			if (from.x == to.x || Math.abs(from.x - to.x) < Config.showLineSpan) {
				let xVal = from.x;
				if (unPreciseIndexOf(vLines, xVal) < 0) {
					vLines.push(xVal);
				}
			} else if (from.y == to.y || Math.abs(from.y - to.y) < Config.showLineSpan) {
				let yVal = from.y;
				if (unPreciseIndexOf(hLines, yVal) < 0) {
					hLines.push(yVal);
				}
			}
		})
	});
}

export function toFixedVector(vector, num) {
	if (vector instanceof THREE.Vector3) {
		const { x, y, z } = vector;
		vector.set(
			parseFloat(x.toFixed(num)),
			parseFloat(y.toFixed(num)),
			parseFloat(z.toFixed(num)),
		);
	} else if (vector instanceof THREE.Vector2) {
		const { x, y } = vector;
		vector.set(parseFloat(x.toFixed(num)), parseFloat(y.toFixed(num)));
	}
	return vector;
}

export const defaultAreaColor = '#f78920';
export const defaultAreaColorCode = mapColorCssToCode.get(defaultAreaColor);
export const defaultSurfaceColor = '#2c6eaa';
export const defaultSurfaceColorCode = mapColorCssToCode.get(defaultSurfaceColor);
export const areaMtl = new THREE.MeshBasicMaterial({
	color: defaultAreaColor,
	transparent: true,
	opacity: 0.8,
	side: THREE.DoubleSide,
	polygonOffset: true,
	polygonOffsetFactor: -1,
	polygonOffsetUnits: -1,
	wireframe: false,
})

export const holeMtl = new THREE.MeshBasicMaterial({
	color: '#f00',
	wireframe: true,
	transparent: true,
	opacity: 0.5,
	side: THREE.DoubleSide,
})

/**
 * 获取工作区的包围矩形
 * @param {{x: number, y: number}[]} contour
 */
export function getMeshRect(contour) {
	const vectors = contour.map(it => new THREE.Vector3(it.x, it.y, 0))
	const box3 = new THREE.Box3()
	box3.setFromPoints(vectors)
	return box3
}

/**
 *
 * @param {THREE.Plane} plane  当前孔洞 或 工作区属于的几何平面
 * @param {WorkAreaMesh[] | HoleMesh[]} meshArr  每个元素必须有属性 vo
 * @param {THREE.Box3[]} boundingBoxes 当前孔洞 或 工作区的本地包围盒。这里用数组而不是单个元素的原因：当孔洞发生形变后，需要同时考虑形变前的包围盒子 与 形变后的包围盒子
 * @return {THREE.Mesh[]} 返回包围盒重叠的mesh, 并且可以认为这些mesh 都处于同一个几何平面
 */
export function getInvolvedMeshes(plane, meshArr, boundingBoxes) {
	const facing = plane.normal
	const involvedMeshes = []
	meshArr.forEach(mesh => {
		const areaData = mesh.vo
		const areaBox3 = areaData.localBox3
		if ((areaData.facing.manhattanDistanceTo(facing) < 0.0001)) {
			const areaPlane = areaData.createPlane()
			const planeDistance = Math.abs(plane.constant - areaPlane.constant)
			if (planeDistance > WorldConfig.samePlaneTolerance) {
				// 两个工作区，它们的朝向虽然相同，但是它们所在几何平面的距离过大，也认为不是相关联的
				return
			}
			if (boundingBoxes && boundingBoxes.length) {
				let intersectedBox = boundingBoxes.find(box => box2Intersects(box, areaBox3))
				if (intersectedBox) {
					involvedMeshes.push(mesh)
				}
			} else {
				involvedMeshes.push(mesh);
			}
		}
	})
	return involvedMeshes
}
